package com.unibeta.cloudtest.springboot;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;

import com.unibeta.cloudtest.CloudTestOutput;
import com.unibeta.cloudtest.CloudTestService;
import com.unibeta.cloudtest.config.plugin.PluginConfigProxy;
import com.unibeta.cloudtest.config.plugin.elements.SpringBeanFactoryPlugin;
import com.unibeta.cloudtest.config.plugin.elements.impl.SpringBeanFactoryPluginImpl;
import com.unibeta.cloudtest.restful.LocalRESTfulTestController;
import com.unibeta.cloudtest.servlet.CloudTestServlet;
import com.unibeta.cloudtest.springboot.config.AppContext;
import com.unibeta.cloudtest.springboot.config.CloudTestProperties;
import com.unibeta.cloudtest.springboot.util.StarterUtils;
import com.unibeta.cloudtest.tool.Tools;
import com.unibeta.cloudtest.util.CloudTestUtils;
import com.unibeta.cloudtest.util.ObjectDigester;
import com.unibeta.vrules.servlets.URLConfiguration;
import com.unibeta.vrules.servlets.VRules4jServlet;
import com.unibeta.vrules.utils.CommonUtils;

/**
 * 
 * <pre>
 	<dependency>
		<groupId>com.unibeta</groupId>
		<artifactId>vRules4j</artifactId>
		<version>3.4.1</version>
	</dependency>
	<dependency>
		<groupId>com.unibeta</groupId>
		<artifactId>cloudtest</artifactId>
		<version>2.0.0.b20220831235156</version>
	</dependency>
	<dependency>
		<groupId>com.mashape.unirest</groupId>
		<artifactId>unirest-java</artifactId>
		<version>1.4.9</version>
	</dependency>
	<dependency>
		<groupId>xpp3</groupId>
		<artifactId>xpp3</artifactId>
		<version>1.1.3.4.M</version>
	</dependency>
 * </pre>
 *
 */
@Configuration("cloudtestSpringBootStarter")
@EnableConfigurationProperties(CloudTestProperties.class)
@ConditionalOnProperty(prefix = "cloudtest", name = "enabled", havingValue = "true", matchIfMissing = true)
@RestController
public class Starter implements EnvironmentPostProcessor {

	@Autowired
	WebApplicationContext applicationContext;

	@Autowired
	CloudTestProperties cloudTestProperties;

	static Boolean enabled = null;

	@PostMapping(value = { "/cloudtest/console", "/cloudtest/services", "/cloudnative/console",
			"/cloudnative/services" }, produces = "application/xml;charset=UTF-8")
	public String console(@RequestBody String request, @RequestHeader(required = false) HttpHeaders headers) {
		boolean isTokenValid = checkToken(headers);

		if (isTokenValid && isEnabled()) {
			return LocalRESTfulTestController.invoke(request);
		} else {
			return "None service is available";
		}

	}

	private boolean checkToken(HttpHeaders headers) {
		boolean isTokenValid = false;

		if (!CommonUtils.isNullOrEmpty(cloudTestProperties.getAccessTokenName()) && headers != null) {
			if (headers.containsKey(cloudTestProperties.getAccessTokenName())) {
				try {
					String token = headers.getFirst(cloudTestProperties.getAccessTokenName());
					isTokenValid = StarterUtils.authToken(token);
					
//					String decoded = new String(Base64.getDecoder().decode(token)).replaceAll("\\D", "");
//
//					if (Tools.isNumeric(decoded)) {
//						Long data = Long.valueOf(decoded);
//						isTokenValid = new Date(data).after(new Date());
//					}
				} catch (Throwable e) {
					isTokenValid = false;
				}
			} else {
				isTokenValid = false;
			}
		} else {
			isTokenValid = true;
		}
		return isTokenValid;
	}

	public boolean isEnabled() {

		if (enabled == null) {
			boolean forceDisabled = false;
			String[] activeProfiles = applicationContext.getEnvironment().getActiveProfiles();

			if (activeProfiles != null && activeProfiles.length > 0) {
				List<String> activeProfileList = Arrays.asList(activeProfiles);
				activeProfileList.stream().forEach(p -> {
					p = p.toLowerCase();
				});

				Collection<String> disabledProfilesList = Arrays
						.asList(cloudTestProperties.getDisabledOnActiveProfiles());
				disabledProfilesList.stream().forEach(p -> {
					p = p.toLowerCase();
				});
				for (String s : activeProfileList) {
					forceDisabled = disabledProfilesList.contains(s);
					if (forceDisabled) {
						break;
					}
				}
			}

			enabled = cloudTestProperties != null && !"false".equalsIgnoreCase(cloudTestProperties.getEnabled())
					&& !forceDisabled;
		}

		return enabled;
	}

	private void runStarterFile() {
		InputStream inputStream = null;
		InputStream dataInputStream = null;

		try {
			inputStream = Thread.currentThread().getContextClassLoader()
					.getResourceAsStream(cloudTestProperties.getStarterFile());
			if (inputStream != null) {
				byte[] bytes = new byte[inputStream.available()];
				dataInputStream = new DataInputStream(inputStream);
				dataInputStream.read(bytes);

				String starterFileString = new String(bytes);
				CloudTestOutput output = new CloudTestService().doTest(starterFileString);

				if (!output.getStatus()) {
					Tools.println("cloudtest running starter file failed,\n", output);
				}
			}
		} catch (Exception e) {
			// empty
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					// empty
				}
			}
			if (dataInputStream != null) {
				try {
					dataInputStream.close();
				} catch (IOException e) {
					// empty
				}
			}
		}
	}

	@Bean
	ServletRegistrationBean cloudTestServletRegistrationBean() {

		if (isEnabled()) {
			AppContext.setApplicationContext(applicationContext);
			SpringBeanFactoryPluginImpl.setApplicationContext(applicationContext);
			PluginConfigProxy.setPluginObject(SpringBeanFactoryPlugin.class.getCanonicalName(), new AppContext());

			onStartup();

			ServletRegistrationBean bean = new ServletRegistrationBean(new CloudTestServlet());
			bean.addInitParameter("CLOUDTEST_HOME$PathProvider",
					"com.unibeta.cloudtest.config.impl.CLOUDTEST_HOME$PathProviderImpl");
			bean.addInitParameter("ROOT_FOLDER_NAME", "cloudtest");
			bean.addInitParameter("WEBSERVICE_ENDPOINT_ADDRESS", "null");
			
			Object endpoint = cloudTestProperties.getEndpoint();
			try {
				endpoint = ObjectDigester.fromJava(cloudTestProperties.getEndpoint());
				if(endpoint != null && !endpoint.toString().trim().startsWith("/")) {
					endpoint  = "/" + endpoint.toString().trim();
				}else {
					endpoint = cloudTestProperties.getEndpoint();
				}
			} catch (Exception e) {
				//passby
			}
			bean.addUrlMappings(endpoint.toString().trim());
			bean.addUrlMappings("/"+ CloudTestUtils.getMD5code(endpoint.toString().trim().getBytes()));
			bean.setLoadOnStartup(5);

			return bean;
		} else {
			return new ServletRegistrationBean(new CloudTestServlet(), "/" + UUID.randomUUID().toString());
		}
	}

	public void onStartup() {
		runStarterFile();
	}

	@Bean
	ServletRegistrationBean vRules4jServletServletRegistrationBean() {

		if (isEnabled()) {

			onShutdown();

			ServletRegistrationBean bean = new ServletRegistrationBean(new VRules4jServlet());
			bean.addUrlMappings("/vrules4j/*");
			bean.setLoadOnStartup(1);

			return bean;
		} else {
			return new ServletRegistrationBean(new VRules4jServlet(), "/" + UUID.randomUUID().toString());
		}

	}

	private void onShutdown() {
		clearSpringBootJarClasspath();
	}

	private void clearSpringBootJarClasspath() {
		if (!CommonUtils.isNullOrEmpty(Tools.getXspringboot())) {
			try {
				// if in spring-boot app, cleanup temp folder contents before shutdown
				// gracefully
				Method getShutdownHandlers = SpringApplication.class.getMethod("getShutdownHandlers");
				if (getShutdownHandlers != null) {
					SpringApplication.getShutdownHandlers().add(new Thread(() -> {
						URLConfiguration.clearSpringBootJarClasspath();
					}));
				}
			} catch (NoSuchMethodException | SecurityException e) {
				// empty
			}
		}
	}

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

		Properties props = new Properties();

		try {
			InputStream inputStream = Thread.currentThread().getContextClassLoader()
					.getResourceAsStream("cloudtest.properties");
			if (inputStream != null) {
				props.load(inputStream);
				PropertySource<?> prop = new PropertiesPropertySource("cloudtest.properties", props);
				environment.getPropertySources().addFirst(prop);
			}
		} catch (IOException e) {
			// pass by
		}
	}
}
